package com.xinxin.socket.sample.handler;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.xinxin.base.util.HttpClientUtil;
import com.xinxin.socket.constant.CommandId;
import com.xinxin.socket.context.ChannelLoginContext;
import com.xinxin.socket.handler.NetMsgHandler;
import com.xinxin.socket.msg.NetMsg;
import com.xinxin.socket.proto.Login;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.glassfish.jersey.client.ClientConfig;

import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Map;

/**
 * @author ys
 * @date 2020/8/7
 */
@Slf4j
public class SampleNetMsgHandler extends NetMsgHandler {

    private static final String LOCAL_URL = "http://localhost:8080/";
    private static final Map<Integer, String> CMD_PATH_MAP = Maps.newHashMap();

    static {
        CMD_PATH_MAP.put(CommandId.SYS_HEART_PING, "server/marsUpdateUserState");
        CMD_PATH_MAP.put(CommandId.USER_LOGIN, "server/login");
    }

    private final ChannelLoginContext channelLoginContext = ChannelLoginContext.getInstance();

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        ctx.fireChannelActive();

        ctx.channel().closeFuture().addListener(future -> channelLoginContext.removeToken(ctx.channel()));
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, NetMsg netMsg) throws Exception {
        switch (netMsg.getCmdId()) {
            case CommandId.SYS_HEART_REPLY:
                if (channelLoginContext.isLogin(ctx.channel())) {
                    Map<String, Object> params = Maps.newHashMap();
                    params.put("userId", channelLoginContext.getLoginId(ctx.channel()));
                    params.put("cmdId", CommandId.SYS_HEART_PING);

                    doHttpPostMethod(CommandId.SYS_HEART_PING, params);
                }

                break;
            case CommandId.USER_LOGIN:
                InputStream requestDataStream = new ByteArrayInputStream(netMsg.getBodyBytes());
                InputStream inputStream = doHttpRequest(CommandId.USER_LOGIN, requestDataStream);

                if (inputStream != null) {
                    NetMsg resp = new NetMsg()
                            .setCmdId(CommandId.USER_LOGIN)
                            .setSeq(netMsg.getSeq())
                            .option(netMsg.getOptionBytes())
                            .body(IOUtils.toByteArray(inputStream));
                    ctx.writeAndFlush(resp).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);

                    Login.LoginResponse response = Login.LoginResponse.parseFrom(resp.getBodyBytes());
                    if (response.getCode() == Login.LoginResponse.Code.CODE_OK_VALUE) {
                        channelLoginContext.joinToken(ctx.channel(), (long) response.getUserId());
                    } else {
                        ctx.close();
                    }
                } else {
                    ctx.close();
                }

                break;
            default:
                log.warn("Invalid cmdId: {}", netMsg.getCmdId());
        }
    }

    /**
     * redirect request to webserver
     *
     * <p>
     * 利用 jetty 的 web 容器，往 response 中写入数据
     */
    private InputStream doHttpRequest(int cmdId, InputStream in) {
        return ClientBuilder.newClient(new ClientConfig())
                .target(LOCAL_URL)
                .path(CMD_PATH_MAP.get(cmdId))
                .request(MediaType.APPLICATION_OCTET_STREAM)
                .post(Entity.entity(in, MediaType.APPLICATION_OCTET_STREAM), InputStream.class);
    }

    private String doHttpPostMethod(int cmdId, Map<String, Object> params) {
        String url = LOCAL_URL + CMD_PATH_MAP.get(cmdId);
        return HttpClientUtil.create().doPostJson(url, JSONObject.toJSONString(params));
    }
}
